<?
/**
 * Gravy Caching Class.
 *
 * This class is a caching class used to cache data that would otherwise be too resource expensive to run on-the-fly.
 *
 * @author Lars Sorhus (lars@saucecode.com.au)
 * @internal It's not recommended at all to modify this code.
 * @package cacher
 */
class cacher extends model{
	/**
	 * <b>Timeout</b>
	 * The number of seconds to cache the data for.<br />
	 * Note: <=0 seconds or false will turn caching off.
	 * @var integer
	 */
	public $timeout=300;

	/**
	 * <b>Serialise</b>
	 * Do you want to serialise/unserialise data being cached? True/False.
	 * It is handy to serialise data that contained in objects, so that they can be unserialised, re-instantiated and used on return.
	 * Note: as of PHP5.2.x you cannot serialise and unserialise simpleXml objects.
	 * @var boolean
	 */
	public $serialise=true;

	/**
	 * <b>Use Database Caching</b>
	 * If useDbCache=true then the database (CACHE_DB with a table of "cache") will be used to cache the data.
	 * Otherwise the filesystem is used to store the cached items as files.
	 * @var boolean
	 */
	public $useDbCache=false;

	/**
	 * <b>Cache Directory</b>
	 * If useDbCache=false then the filesystem is used to store the cached items as files.
	 * It's best to use full paths to the directory where the cache will be stored, and ensure chmod777 is applied to the directory in question.
	 * @var string
	 */
	public $directory=NULL;

	/**
	 * <b>Caching Database</b>
	 * The caching table MUST BE "cache", but the caching database can be anything and by default is the constant CACHE_DB.
	 * This option is used if useDbCache=true (default).
	 * @var string
	 */
	public $database=NULL;

	/**
	 * <b>Caching Table</b>
	 * The caching table MUST BE "cache", but the caching database can be anything and by default is the constant CACHE_DB.
	 * This option is used if useDbCache=true (default).
	 * @var string
	 */
	public $table="cache";

	/**
	 * <b>Data Age</b>
	 * This is a variable that holds the age of the data that has been cached (in seconds).
	 * @var string
	 */
	public $ageOfData=NULL;

	/**
	 * <b>Data Age</b>
	 * This is a variable that holds the age of the data that has been cached (in seconds).
	 * @var string
	 */
	public $cache_id=0;


	public function __construct(){
		parent::__construct("cache");
		$this->useDbCache=(strtolower(CACHE_METHOD)=="database");
		$this->directory=CACHE_DIR;
		$this->database=CACHE_DB;
		$this->timeout=(int)CACHE_DURATION;
	}

	public function clean(){
		shell_exec("rm -f {$this->directory}*.cached");
	}

	public function cache($key,$data,$timeout=NULL,$serialise=NULL){
		$result=false;
		if($this->useDbCache){
			$result=$this->__dbCache($key,$data,$timeout,$serialise);
		}else{
			$result=$this->__filesystemCache($key,$data,$timeout,$serialise);
		}
		return($result);
	}

	public function load($key,$timeout=NULL,$serialise=NULL){
		$result=false;
		if($this->useDbCache){
			$result=$this->__dbLoad($key,$timeout,$serialise);
		}else{
			$result=$this->__filesystemLoad($key,$timeout,$serialise);
		}
		return($result);
	}

	private function __dbCache($key,$data,$timeout=NULL,$serialise=NULL){
		$result=false;

		$this->timeout=($timeout===NULL)?$this->timeout:$timeout;
		$this->serialise=($serialise===NULL)?$this->serialise:$serialise;

		#$this->debug("Caching Data in Database",array("key"=>$key,"data"=>$data,"timeout"=>$this->timeout,"serialise"=>$this->serialise,"CACHE_DURATION"=>CACHE_DURATION));

		if($this->timeout>0){
			$md5key=@md5($key);

			if($this->serialise){
				$data=serialize($data);
			}

			if($this->dbConnect($this->database)){
				#$this->debug("Connected to Cache Database");
				if((int)$this->cache_id>0){
					#$this->debug("Updating existing cache");
					$sQl="
						UPDATE
							".$this->table."
						SET
							cached=".$this->db->escape($data).",
							dateUpdated=UNIX_TIMESTAMP()
						WHERE
							id=".$this->cache_id."
							AND md5key=".$this->db->escape($md5key)."
							AND campaign_id=".$this->db->escape((int)CACHE_CAMPAIGN_ID)."
							AND status=1
						;
					";
					if($this->db->exec($sQl)){
						$result=true;
					}else{
						$this->addError("Error Updating Cache","cacher::cache::__dbCache::errorWithSqlUpdate",array("timeout"=>$this->timeout,"serialise"=>$this->serialise,"data"=>$data,"sQl"=>$sQl));
					}
				}
				if($result!==TRUE){
					#$this->debug("Creating new cache");
					$sQl="
						INSERT INTO
							".$this->table."(
								md5key,
								cached,
								campaign_id,
								status,
								dateAdded,
								dateUpdated
							)
							VALUES(
								".$this->db->escape($md5key).",
								".$this->db->escape($data).",
								".$this->db->escape((int)CACHE_CAMPAIGN_ID).",
								1,
								UNIX_TIMESTAMP(),
								UNIX_TIMESTAMP()
							)
						;
					";
					if($this->db->exec($sQl)){
						$result=true;
					}else{
						$this->addError($this->ERROR_CACHE_SAVE,"cacher::cache::__dbCache::errorWithSqlInsert",array("timeout"=>$this->timeout,"serialise"=>$this->serialise,"data"=>$data,"sQl"=>$sQl));
					}
				}
			}else{
				$this->addError($this->ERROR_CACHE_SAVE,"cacher::cache::__dbCache::dbConnectionError",array("timeout"=>$this->timeout,"serialise"=>$this->serialise,"data"=>$data));
			}
			$this->dbClose();
		}else{
			$result=true;
		}
		return($result);
	}

	private function __dbLoad($key,$timeout=NULL,$serialise=NULL){
		$result=false;

		$this->timeout=($timeout===NULL)?$this->timeout:$timeout;
		$this->serialise=($serialise===NULL)?$this->serialise:$serialise;

		#$this->debug("Loading existing cache");

		if($this->timeout>0){
			$md5key=@md5($key);

			if($this->dbConnect($this->database)){
				$sQl="
					SELECT
						id,
						cached,
						campaign_id,
						status,
						dateAdded,
						dateUpdated
					FROM
						cache
					WHERE
						md5key=".$this->db->escape($md5key)."
						AND campaign_id=".$this->db->escape((int)CACHE_CAMPAIGN_ID)."
						AND status=1
					ORDER BY
						dateUpdated DESC
					LIMIT
						0,1
				";
				if($this->db->exec($sQl,true)){
					$row=$this->db->rows[0];
					$this->cache_id=$row["id"];
					#$this->debug("Loading existing cache SUCCESS",$sQl);
					if($row["dateUpdated"]>time()-$this->timeout){
						$this->ageOfData=time()-$row["dateUpdated"];
						$result=$row["cached"];
						if(strlen((string)$result)>0){
							if($this->serialise){
								$result=unserialize($result);
							}
						}else{
							$this->addError("Cache Not Valid","cacher.load.errorWithCachedData",array("timeout"=>$this->timeout,"serialise"=>$this->serialise,$row));
						}
					}
				}
			}else{
				$this->addError("Database Connection Error","cacher::load::dbConnectionError",array("timeout"=>$this->timeout,"serialise"=>$this->serialise,"key"=>$key,"data"=>$data));
			}
			$this->dbClose();
		}
		return($result);
	}

	private function __filesystemCache($key,$data,$timeout=NULL,$serialise=NULL){
		$result=false;

		$this->timeout=($timeout===NULL)?$this->timeout:$timeout;
		$this->serialise=($serialise===NULL)?$this->serialise:$serialise;

		if($this->timeout>0){
			$cached_file=$this->directory.@md5($key).".cached";

			if($this->serialise){
				$data=serialize($data);
			}

			if(@file_put_contents($cached_file,$data)){
				$result=true;
			}else{
				$this->addError($this->ERROR_CACHE_SAVE,"cacher.load.errorSavingCacheData",array("timeout"=>$this->timeout,"serialise"=>$this->serialise,"cached_file"=>$cached_file,"data"=>$data));
			}
		}else{
			$result=true;
		}
		return($result);
	}

	private function __filesystemLoad($key,$timeout=NULL,$serialise=NULL){
		$result=false;

		$this->timeout=($timeout===NULL)?$this->timeout:$timeout;
		$this->serialise=($serialise===NULL)?$this->serialise:$serialise;

		if($this->timeout>0){
			$cached_file=$this->directory.@md5($key).".cached";

			if(@file_exists($cached_file)){
				$last_modified=@filemtime($cached_file);

				if($last_modified>time()-$this->timeout){
					$this->ageOfData=time()-$last_modified;
					$result=@file_get_contents($cached_file);
					if($result){
						if($this->serialise){
							$result=unserialize($result);
						}
					}else{
						$this->addError($this->ERROR_CACHE_READ,"cacher.load.errorReadingCachedFile",array("timeout"=>$this->timeout,"serialise"=>$this->serialise,"cached_file"=>$cached_file));
					}
				}
			}
		}
		return($result);
	}
};
?>